判断数据类型 Object.prototype.toString.call()

参考:

为什么用Object.prototype.toString.call(obj)检测对象类型

谈谈Object.prototype.toString

为什么用Object.prototype.toString.call()判断数据类型?

typeof不能正确判断数据类型:

1
2
3
4
5
6
7
8
9
10
11
12
var a = null
var b = {}
var c = [1, 2, 3]

typeof a
// "object"

typeof b
// "object"

typeof c
// "object"

使用Object.prototype.toString.call(obj)可以准确判断各种类型(无法判断自定义对象类型,用instanceof判断自定义类型):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Object.prototype.toString.call('hello world') 
// "[object String]"

Object.prototype.toString.call(123)
// "[object Number]"

Object.prototype.toString.call(true)
// "[object Boolean]"

Object.prototype.toString.call(null)
// "[object Null]"

Object.prototype.toString.call(undefined)
// "[object Undefined]"

Object.prototype.toString.call(function(){})
// "[object Function]"

Object.prototype.toString.call({})
// "[object Object]"

Object.prototype.toString.call([])
// "[object Array]"

Object.prototype.toString.call(Math)
// "[object Math]"

Object.prototype.toString.call(new Date)
// "[object Date]"

Object.prototype.toString.call(/\d/)
// "[object RegExp]"

// 自定义类型
function Person(){}
Object.prototype.toString.call(new Person)
// "[object Object]"

为什么不直接用obj.toString()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
"hello world".toString()
// "hello world"

(123).toString()
// "123"

[1, 2, 3].toString()
// "1,2,3"

function(){}.toString()
// "function(){}"

new Date().toString()
// "Mon Sep 16 2019 09:07:13 GMT+0800 (中国标准时间)"

null.toString()
// "error"

undefined.toString()
// "TypeError: Cannot read property 'toString' of null ..."

因为toStringObject的原型方法,ArrayFunction等类型作为Object的实例,都重写了toString方法。不同的数据类型调用toString方法时,根据原型链的知识,调用的是重写之后的toString方法。因此,想要得到对象的具体类型,应调用Object上原型toString方法。

将数组的toString方法删除,看看会是什么结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var arr = [1, 2, 3]

console.log(Array.prototype.hasOwnProperty("toString"))
// true

console.log(arr.toString())
// "1,2,3"

delete Array.prototype.toString

console.log(Array.prototype.hasOwnProperty("toString"))
// false

console.log(arr.toString())
// "[object Array]"

删除了ArraytoString方法后,再调用arr.toString()方法时,不再有屏蔽Object原型方法的实例方法,因此arr沿着原型链最后调用了ObjecttoString方法,返回了和Object.prototype.toString.call(arr)相同的结果。

ECMAScript 5 Object.prototype.toString() 规范

在ECMAScript 5中,Object.prototype.toString()被调用时,会进行如下步骤:

  • 如果this的值是undefined,返回 [object Undefined]

  • 如果this的值是null,返回 [object Null]

  • O成为调用ToObject(this)的结果

  • class成为O的内部属性 [[Class]]的值

  • 返回三个字符串 "[object", class, 以及"]" 拼接而成的字符串

[[Class]] 是一个内部属性,值为一个类型字符串,表明了该对象的类型。

本规范的每种内置对象都定义了 [[Class]] 内部属性的值。宿主对象的 [[Class]] 内部属性的值可以是除了 “Arguments”、”Array”、”Boolean”、”Date”、”Error”、”Function”、”JSON”、”Math”、”Number”、”Object”、”RegExp”、”String” 的任何字符串。(出自ES5/类型)

在JavaScript代码里,唯一可以访问[[Class]]属性的方法就是通过Object.prototype.toString。可以用下列函数,来获取任意变量的[[Class]]属性:

1
2
3
4
function getClass (a) {
const str = Object.prototype.toString.call(a)
return /^\[object (.*)\]$/.exec(str)[1]
}